import { parse, ParsedUrlQuery } from "querystring";

export function testUrl() {
  const param = window.location.pathname.replace("Mango-Pay", "").split("/")[0];
  window.location.href = `${window.location.origin}/${param}/login?from=${window.location.origin}/${param}`;
}

/** 重载签名编写函数,实现签名编写内容
 * @description 获取请求上边的参数
 *
 *
 */
export function getParams(params?: { str?: string }): ParsedUrlQuery;
export function getParams(params: {
  str?: string;
  key: string;
}): string | undefined;
export function getParams(param?: { str?: any; key?: any }): any {
  const { str, key } = param ?? {};
  const query = parse(str || window.location?.search?.replace(/\?/, ""));
  if (key) return query[key];
  return query;
}

export class Test {
  public name: string;
  constructor(name: string) {
    this.name = name;
  }
}

export const rotatle = (arr: number[], k: number) => {
  const length = arr?.length;
  const steps = Math.abs(k % length);
  console.log(steps);

  for (let i = 0; i < steps; i++) {
    const n = arr.pop();
    if (n != null) {
      arr.unshift(n); // 数组是一个有序结构，unshift 操作非常慢！！！ O(n)
    }
  }
  return arr;
};

export class Person {
  public age: string;
  public sex!: string;
  public phone: string;

  constructor(public name: string, age: string, phone: string, sex?: string) {
    // this.sex = sex;
    this.name = name;
    this.age = age;
    this.phone = phone;
  }

  doEat(): number {
    return 3;
  }
  /**
   * void 默认没有返回值。
   */
  public play(): void | string {
    return this.name;
  }
}

type A = {
  id: number;
  name: string;
};

const arr: A[] = [
  {
    id: 1,
    name: "zs",
  },
  {
    id: 2,
    name: "ls",
  },
];

/**
 * 函数重载
 * @param param 参数类型
 */
export function sendMsg(param: number): A;

export function sendMsg(param: string): A[];

export function sendMsg(param: any) {
  if (typeof param === "number") {
    return arr.find((item) => item.id === param);
  }
  return arr.filter((item) => item.name === "zs");
}
/**
 * 实现arrayList
 */
export class ArrayList {
  constructor(public list: Array<object>) {}

  public get(idx: number) {
    return this.list[idx];
  }
  /**
   * 实现签名参数个数可以少于重载签名的参数个数，但实现签名如果准备包含重载签名的某个位置的参数
   * ，那实现签名就必须兼容所有重载签名该位置的参数类型【联合类型或 any 或 unknown 类型的一种】。
   * @param num
   */
  public remove(num: number): number;

  public remove(num: object): object;

  public remove(num: any): unknown {
    this.list = this.list.filter((item, idx) => {
      if (typeof num === "number") {
        return !(num === idx);
      }
      return item === num;
    });

    return num;
  }
}

/**
 * 代码质量。
 */

/**
 * 定义单例模式
 */

export class Area {
  public width: number;
  public height: number;
  constructor(width: number, height: number);
  constructor(objValue: { height: number; width: number });

  constructor(value: any, height: number = 0) {
    if (typeof value === "number") {
      this.width = value;
      this.height = height;
    } else {
      this.height = value?.height;
      this.width = value?.width;
    }
  }

  public getArea() {
    return this.width * this.height;
  }
}
/**
 * 答：任何一个 TS 类中的静态成员存储在内存的静态区，运行一个 TS 类，TS首先会为静态成员开辟内存空间，静态成员的内存空间分配的时间要早于对象空间的分配
 * ，也就是任何一个对象创建之前 TS 就已经为静态成员分配好了空间。但一个静态方法或静态属性只会分配一个空间，
 * 只要当前服务器不重启或控制台程序还没有结束之前【如果是开发期间临时测试，一般用控制台】，那么静态方法或者是静态属性就一直存在内存空间，
 * 无论调用多少次这个静态方法或静态属性，都是调用的同一块空间。
 */
/** 单例模式， 静态方法共享同一份数据。 */
export class MyLocal {
  static myLocal: MyLocal;

  private constructor() {
    console.log("私有构造器");
  }

  public setItem(key: string, value: any) {
    value = JSON.stringify(value);
    localStorage.setItem(key, value);
  }
  /**
   * 每次调用都会返回一个 对象
   * //   1. 带static关键字的方法就是一个静态方法
  //   2. 静态方法和对象无关，外部的对象变量不能调用静态方法和静态属性，
  //   3. 外部可以通过类名来调用
   * @returns
   */
  public static getInstance() {
    // const obj = new MyLocal();

    if (!this.myLocal) {
      this.myLocal = new MyLocal();
    }

    return this.myLocal;
  }

  public getItem(key: string) {
    let value = localStorage.getItem(key);

    return value === null ? null : JSON.parse(value);
  }
}

/**
 * 斐波那契诶
 */
export const fibonacci = (num: number): number => {
  if (num < 0 || num === undefined) {
    return -1;
  }
  if (num === 0) {
    return 0;
  }

  if (num === 1) {
    return 1;
  }

  return fibonacci(num - 1) + fibonacci(num - 2);
};

export const fibonacci2 = (num: number): number => {
  if (num < 0 || num === undefined) {
    return -1;
  }
  if (num === 0) {
    return 1;
  }

  if (num === 1) {
    return 1;
  }
  let n1 = 1;
  let n2 = 1;
  let res: number = -1;

  for (let i = 2; i < num; i++) {
    res = n2 + n1;
    n1 = n2;
    n2 = res;
  }

  return res;
};

/**
 * 手写事件总线
 */
export class EventBus {
  public eventBusList: {
    [key: string]: Array<{ func: Function; once: boolean }>;
  } = {};

  public on(key: string, func: Function, once: boolean = false) {
    const event = this.eventBusList[key] ?? [];
    if (event.length > 0) {
      event.push({
        func,
        once,
      });
    }
    this.eventBusList[key] = event;
  }
  public once(key: string, func: Function) {
    this.on(key, func, true);
  }

  public emit(key: string, ...args: any[]) {
    const list = this.eventBusList[key];

    if (!list?.length) return;

    this.eventBusList[key] = list?.filter((item) => {
      const { func, once } = item;
      func(...args);
      return once ? false : true;
    });
  }

  public off(key: string, func?: Function) {
    const list = this.eventBusList[key];
    if (func) {
      list.filter((item) => item?.func === func);
    } else {
      this.eventBusList[key] = [];
    }
  }
}

type FlattenData = Array<number | FlattenData>;

/**
 * flatten
 */
const flatten = (arr: FlattenData) => {
  const list: number[] = [];

  arr.forEach((item) => {
    if (typeof item === "number") {
      list.push(item);
    } else {
      const sonList = flatten(item);
      list.concat(sonList);
    }
  });

  return list;
};

/**
 * 判断类型(call,apply 是直接调用函数)
 */

export const getType = (someOne: any) => {
  if (someOne === undefined || someOne === null) {
    return "";
  }
  const data = Object.prototype.toString.call(someOne);
  const index = data.indexOf(" ");
  return data.slice(index + 1, -1).toLocaleLowerCase();
};

/**
 * 手写new
 * Object.create() 方法用于创建一个新对象，使用现有的对象来作为新创建对象的原型（prototype）。
 * Object.create(proto) proto
新创建对象的原型对象。
 */

// export function Cat(name: string, age: number) {
//   this.name = name;

//   this.age = age;
// }

// Cat.prototype.eat = () => {
//   console.log(this.name + "在吃鱼");
// };

/**
 * Number.isNaN(deduction)
 */
/**
 * 编写链式调用方案LazyMan
 * 
 * 调用方案
 *  const me = new LazyMan("张三");
    me.eat("苹果")
      .eat("香蕉")
      .sleep(5)
      .eat("葡萄")
      .sleep(4)
      .eat("西红柿")
      .eat("黄瓜"); // 打印结果如下
 */
export class LazyMan {
  private tasks: Function[] = [];
  constructor(public name: string) {
    setTimeout(() => {
      this.next();
    });
  }

  next() {
    const fn = this.tasks.shift();
    fn?.();
  }

  eat(str: string) {
    const task = () => {
      console.log(`在吃${str}`);
      this.next();
    };
    this.tasks.push(task);
    return this;
  }
  sleep(num: number) {
    const task = () => {
      const interval = setInterval(() => {
        console.log(`休息${num}秒后开始`);
        num--;
        if (num === -1) {
          clearInterval(interval);
          this.next();
        }
      }, 1000);
    };
    this.tasks.push(task);
    return this;
  }
}

/**
 * curry
 */

export const curry = (num: any): any => {
  let num1 = num;

  const add = (newNum: number) => {
    if (newNum) {
      num1 += newNum;
      return add;
    } else {
      return num1;
    }
  };

  return add;
};

interface IArrayItem {
  id: number;
  name: string;
  parentId: number;
}

const arr1: IArrayItem[] = [
  { id: 1, name: "部门A", parentId: 0 }, // 0 代表顶级节点，无父节点
  { id: 2, name: "部门B", parentId: 1 },
  { id: 3, name: "部门C", parentId: 1 },
  { id: 4, name: "部门D", parentId: 2 },
  { id: 5, name: "部门E", parentId: 2 },
  { id: 6, name: "部门F", parentId: 3 },
];

interface ITreeNode {
  id: number;
  name: string;
  children?: ITreeNode[];
}

const convert = (arr: IArrayItem[]) => {
  const map: Map<number, ITreeNode> = new Map();

  arr.forEach((item) => {
    const { id, name, parentId } = item;
    const threnode: ITreeNode = { id, name };
    map.set(id, threnode);
    let root: ITreeNode | null = null;
    const parentnode = map.get(parentId);
    if (parentnode) {
      if (parentnode.children == null) {
        parentnode.children = [];
      }
      parentnode.children?.push(threnode);
    }

    // 找到根节点
    if (parentId === 0) root = threnode;
  });
  return null;
};

function visitNode(n: Node) {
  if (n instanceof Comment) {
    // 注释
    console.info("Comment node ---", n.textContent);
  }
  if (n instanceof Text) {
    // 文本
    const t = n.textContent?.trim();
    if (t) {
      console.info("Text node ---", t);
    }
  }
  if (n instanceof HTMLElement) {
    // element
    console.info("Element node ---", `<${n.tagName.toLowerCase()}>`);
  }
}

/**
 * 深度优先遍历
 * @param root dom node
 */
function depthFirstTraverse1(root: Node) {
  visitNode(root);

  const childNodes = root.childNodes; // .childNodes 和 .children 不一样
  if (childNodes.length) {
    childNodes.forEach((child) => {
      depthFirstTraverse1(child); // 递归
    });
  }
}

/**
 * 深度优先遍历
 * @param root dom node
 */
function depthFirstTraverse2(root: Node) {
  const stack: Node[] = [];

  // 根节点压栈
  stack.push(root);

  while (stack.length > 0) {
    const curNode = stack.pop(); // 出栈
    if (curNode == null) break;

    visitNode(curNode);

    // 子节点压栈
    const childNodes = curNode.childNodes;
    if (childNodes.length > 0) {
      // reverse 反顺序压栈
      Array.from(childNodes)
        .reverse()
        .forEach((child) => stack.push(child));
    }
  }
}

/**
 * 广度优先遍历
 * @param root dom node
 */
function breadthFirstTraverse(root: Node) {
  const queue: Node[] = []; // 数组 vs 链表

  // 根节点入队列
  queue.unshift(root);

  while (queue.length > 0) {
    const curNode = queue.pop();
    if (curNode == null) break;

    visitNode(curNode);

    // 子节点入队
    const childNodes = curNode.childNodes;
    if (childNodes.length) {
      childNodes.forEach((child) => queue.unshift(child));
    }
  }
}

// const box = document.getElementById('box')
// if (box == null) throw new Error('box is null')
// depthFirstTraverse2(box)

// 深拷贝
// function cloneDeep(obj: any) {
//     if (typeof obj !== 'object' || obj == null ) return obj

//     let result: any
//     if (obj instanceof Array) {
//         result = []
//     } else {
//         result = {}
//     }

//     for (let key in obj) {
//         if (obj.hasOwnProperty(key)) {
//             result[key] = cloneDeep(obj[key])
//         }
//     }

//     return result
// }

// const a: any = {
//     set: new Set([10, 20, 30]),
//     map: new Map([['x', 10], ['y', 20]])
// }
// a.self = a
// console.log( cloneDeep(a) )

// json.stringy  会丢失函数，或者是循环引用时候调用会报错

//深拷贝
export function cloneDeep(obj: any, map = new WeakMap()): any {
  // obj == undefined  或者是 obj == null
  if (typeof obj !== "object" || obj == null) return obj;

  // 避免循环引用
  const objFromMap = map.get(obj);
  if (objFromMap) return objFromMap;

  let target: any = {};
  map.set(obj, target);

  // Map
  if (obj instanceof Map) {
    target = new Map();
    obj.forEach((v, k) => {
      const v1 = cloneDeep(v, map);
      const k1 = cloneDeep(k, map);
      target.set(k1, v1);
    });
  }

  // Set
  if (obj instanceof Set) {
    target = new Set();
    obj.forEach((v) => {
      const v1 = cloneDeep(v, map);
      target.add(v1);
    });
  }

  // Array
  if (obj instanceof Array) {
    target = obj.map((item) => cloneDeep(item, map));
  }

  // Object
  for (const key in obj) {
    const val = obj[key];
    const val1 = cloneDeep(val, map);
    target[key] = val1;
  }

  return target;
}

// 字符串拼接 (一般转化为同种类型再拼接)

//  100 + '10' =  '10010'
//  true + '10' = 'true10 '
//  undefined + '100' = 'undefined100'

// 结果  12 4

// console.log(1)
// const prom = new Promise((reject,resolve) => {
//     console.log(2)
//     resolve(3)
//     console.log(5)

// })

// console.log(4)

//  1 2 4

// console.log(1)
// new Promise((reject,resolve) => {
//     console.log(2)
//     // resolve(3)
//     throw new Error()
//     console.log(5)

// }).then((num) => {
//     console.log(num)
// },() => {

// })

// console.log(4)

class MyLocalStorage {
  static store: MyLocalStorage | undefined;

  private constructor() {}

  public static getStoreInstance() {
    if (!this.store) {
      this.store = new MyLocalStorage();
    }

    return this.store;
  }

  public setItem(key: string, value: any) {
    localStorage.setItem(key, value);
  }

  public getItem(key: string) {
    return localStorage.getItem(key);
  }
}

const instance = MyLocalStorage.getStoreInstance();
instance.getItem("123");

class One {
  static store: One;

  private constructor() {}

  static getInstance() {
    if (!this.store) {
      this.store = new One();
    }
  }

  public setItem(key: string, item: object) {
    const obj = item ? JSON.stringify(item) : "";
    localStorage.setItem(key, obj);
  }

  public getItem(key: string) {
    const dataStr = localStorage.getItem(key);

    if (dataStr) {
      return JSON.parse(dataStr);
    }
    return null;
  }
}
